"Initialize this instance, setting name to aString"
name := aString.
readMessages := SparseIntegerCollection new.
subjects := nil.! !
!NewsGroup methodsFor: 'comparing'!
= aNewsgroup
(aNewsgroup isMemberOf: self class)
ifTrue: [ ^name = aNewsgroup name ]
ifFalse: [ ^false ]! !
!NewsGroup methodsFor: 'subjects'!
addSubject: aSubject
subjects isNil ifTrue: [ subjects := OrderedCollection new ].
subjects add: aSubject.
aSubject setGroup: self.
self markSubjectAsUnread: aSubject!
allSubjectsReadOrUnread
"Return an array of all NewsSubjects currently on disk"
^(self class named: name) refreshSubjects!
emptyCaches
"Empty cached subject list"
subjects := nil!
newOrExistingSubjectNamed: subjectString
"This returns a NewsSubject representing the subject named subjectString. If I already know of such a subject, this returns that NewsSubject. Otherwise, a new one is created, added to myself, and returned"
'Instances of this class allow reading and browsing and posting messages on UseNet in Smalltalk. By Carl Watts (Carl@ParcPlace.com)
Modified by David Monnie (davidm@slc.com) July 7, 1991.
Added classes:
NewsGroup
NewsMessage
NewsSubject
SparseIntegerCollection
Added features:
Subject pane
Individual messages marked read (rather than just a high-water mark)
Ability to save setup to file in Smalltalk format or .newsrc format
The user interface is very Smalltalk browser like. It allows you to read messages in any order you like just by clicking on the message subject. You can name newsgroups that you wish to subscribe to. And you can easily post new messages to these newsgroups.
Unlike other newsreaders, this one needs to have the message directories of the newsserver mounted on this machines filesystem in order to read/view the messages. This newsreader will look for the message directories under ''/mnt/spool/news'' by default but you can set it to be whereever you like by sending a message to the class. If I ever find out what the protocol is to communicate through a Unix Socket to a newsserver, I can easily modify this newsreader to user that medium. But for now, it needs to access the files directly.
For posting messages, this newsreader uses the standard "inews" command at the OS level (if one can use the term "OS" so broadly as to include Unix). "inews" must be available in the current path.
This newsreader also keeps a standard inews message header that you can customize with your personal information.
When you open a new instance of the receiver, there are three views, two ListViews at the top and a TextView of the bottom. The first ListView lists the newsgroups you currently subscribe to. You can subscribe to new ones by selecting ''subscribe...'' from the menu of this view. The second ListView shows the messages (by Subject) in the currently selected newsgroup (selected in the first ListView). The TextView shows you the text of the message if a message is selected. Or it shows a preformated header to post a message to the currently selected newsgroup, if one is selected. Or if no newsgroup is selected then it shows you the default header for messages. You can change this default header however you like and ''accept'' it.
To post a new message to a newsgroup, select the newsgroup (and don''t select any messages in the newsgroup). The TextView will show a preformatted message header for a new message. Add the text that you wish after the header (leaving at least one blank line between the header and the start of your message). When you are satsified with your message, select ''post'' from the menu and your new message will be posted to that newsgroup.
This class was created by Carl Watts (Carl@ParcPlace.com). If you like this class and use it, send me an email message about it. '!
!NewsReader methodsFor: 'subscribed group list'!
newSubscribedGroupList
"Update the subscribedGroupList."
self changed: #subscribedGroup!
newSubscribedGroupList: initialSelection
"Update the subscribedGroupList and make initialSelection the initial selection."
subscribedGroup := initialSelection.
self newSubscribedGroupList!
subscribedGroup
"Answer the currently selected subscribed group."
^subscribedGroup!
subscribedGroup: aGroup
"Set the currently selected subscribed group."
subscribedGroup := aGroup.
self newSubjectList!
subscribedGroupList
"Answer the list of subscribed groups."
^Array withAll: Subscribed!
subscribedGroupMenu
"Answer the menu for the subscribed group list."
self subscribedGroup isNil ifTrue: [^PopUpMenu
labelList: #(('update') ('subscriptions...') ('save setup to file' 'retrieve setup from file'))
'This collection will hold a large number of Integers while minimizing the actual size of the collection. It does this by storing intervals when possible. For instance, an instance of this class can hold the integers 1 to 100, and 102. It really contains two elements - the interval (1 to: 100), and 102. If 101 is subsequently added, it will contain only one element - the interval (1 to: 102).'!
"Store using '-' and ','. For example, if I represent the numbers 1 to 5 and 7, store myself '1-5,7'"
| first |
first := true.
super do: [ :elem |
first
ifTrue: [ first := false ]
ifFalse: [aStream nextPut: $, ].
(elem isKindOf: Interval)
ifTrue: [
| start stop |
start := elem first.
stop := elem last.
start = stop
ifTrue: [ start storeOn: aStream ]
ifFalse: [
start storeOn: aStream.
aStream nextPut: $-.
stop storeOn: aStream ]]
ifFalse: [
elem storeOn: aStream ]]! !
!SparseIntegerCollection methodsFor: 'private'!
removeInteger: anInteger
"Private: this method assumes anInteger exists in receiver"
| index theInterval first last |
index := self startPositionOf: anInteger.
theInterval := self at: index.
first := theInterval first.
last := theInterval last.
(first == last) ifTrue: [
first == anInteger ifFalse: [ ^self error: 'internal error' ].
self removeAtIndex: index.
^anInteger ].
anInteger == first ifTrue: [
self at: index put: (first + 1 to: last).
^anInteger ].
anInteger == last ifTrue: [
self at: index put: (first to: last - 1).
^anInteger ].
"So, anInteger is in the middle of theInterval. We must break
theInterval into two intervals."
self at: index put: (first to: anInteger - 1).
self add: (anInteger + 1 to: last) beforeIndex: index + 1.
^anInteger!
removeInterval: anInterval
"Private: this method assumes anInterval exists in receiver"
^self error: 'not yet implemented'!
replaceFrom: start to: stop withElement: element
"This is like replaceFrom:to:with:, except: 1) element is a single element to be added, not a collection; and 2), the distance from start to stop can be 1 or greater"
(start to: stop) reverseDo: [ :index | self removeAtIndex: index ].